﻿using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Reflection;

namespace RunAsAdministrator
{
    public class Program
    {
        private static string DEPENDENCY_FILE
        {
            get
            {
                var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    "RunAsAdministrator");
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                return Path.Combine(path, "Interop.IWshRuntimeLibrary.dll");
            }
        }

        public static void Main(string[] args)
        {
            try
            {
                var option = Options.Parse(args);

                //Console.WriteLine($"Program execution paused, attach process(PID: {Process.GetCurrentProcess().Id}) and press ENTER key to continue");
                //Console.ReadLine();
                // 检查参数，如果没有传入参数则显示使用帮助信息
                if (!option.HasArgument)
                {
                    Usage();
                    return;
                }

                // 设置工作目录
                if (!string.IsNullOrWhiteSpace(option.Dir))
                {
                    Environment.CurrentDirectory = option.Dir;
                }

                // 打开程序运行
                if (!string.IsNullOrWhiteSpace(option.Run))
                {
                    // 这里不管是exe程序还是快捷方式，都直接运行
                    if (string.IsNullOrWhiteSpace(option.Args))
                    {
                        Process.Start(option.Run).StartInfo.WorkingDirectory = option.Dir;
                    }
                    else
                    {
                        Process.Start(option.Run, option.Args).StartInfo.WorkingDirectory = option.Dir;
                    }
                    return;
                }

                // 设置环境变量
                if (option.Env)
                {
                    SetEnvironment();
                    return;
                }

                // 创建参数传入程序的快捷方式
                foreach (string item in option.Arguments)
                {
                    CreateDesktopLnk(item);
                }
            }
            catch (Exception ex)
            {
                //Console.WriteLine(ex.Message);
                //Console.WriteLine(ex.StackTrace);
                MessageBox.Show(ex.Message + ex.StackTrace, "Error");
            }
            //Console.ReadLine();
        }

        /// <summary>
        /// 
        /// </summary>
        private static void SetEnvironment()
        {
            // 生成bat文件，放到系统目录下
            var target = Environment.GetFolderPath(Environment.SpecialFolder.System);
            var content = $@"@echo off
set cmd=""{Application.ExecutablePath}"" -run %1 -dir %cd% -args %2
%cmd%
";
            File.WriteAllText(Path.Combine(target, "asadmin.bat"), content);
        }

        /// <summary>
        /// 在桌面创建快捷方式
        /// </summary>
        /// <param name="filename"></param>
        private static void CreateDesktopLnk(string filename)
        {
            if (string.IsNullOrEmpty(filename))
            {
                return;
            }

            filename = Path.GetFullPath(filename);

            // 检查依赖库是否存在
            CheckDependency();
            var asm = Assembly.LoadFrom(DEPENDENCY_FILE);

            var shellType = asm.GetType("IWshRuntimeLibrary.WshShellClass");
            var shotcutType = asm.GetType("IWshRuntimeLibrary.IWshShortcut");

            var shell = asm.CreateInstance("IWshRuntimeLibrary.WshShellClass");

            // 判断一下，如果传入的文件是一个lnk快捷方式文件，那么直接修改快捷方式的路径信息
            if (filename.EndsWith(".lnk", StringComparison.OrdinalIgnoreCase))
            {
                UpdateLnk(shotcutType, shell, filename);
                return;
            }

            // 不是快捷方式，直接将文件路径写入  并且尽量读取文件的“文件说明”(元数据)来作为文件名
            var file = FileVersionInfo.GetVersionInfo(filename);

            // 将快捷方式创建到桌面
            var lnkFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), Path.GetFileName(filename) + ".lnk");
            var srcLnk = InvokeMethod(shellType, shell, "CreateShortcut", lnkFile);

            SetValue(shotcutType, srcLnk, "TargetPath", Application.ExecutablePath);
            SetValue(shotcutType, srcLnk, "Arguments", string.Format("-run \"{0}\"", filename));
            SetValue(shotcutType, srcLnk, "WorkingDirectory", Path.GetDirectoryName(filename));
            SetValue(shotcutType, srcLnk, "Description", file.FileDescription);
            SetValue(shotcutType, srcLnk, "IconLocation", string.Format("{0}, 0", filename));
            SetValue(shotcutType, srcLnk, "WindowStyle", 1);

            InvokeMethod(shotcutType, srcLnk, "Save");
        }

        private static void UpdateLnk(Type shotcutType, object shell, string filename)
        {
            filename = Path.GetFullPath(filename);

            string name = Path.GetFileNameWithoutExtension(filename);

            var srcLnk = InvokeMethod(shotcutType, shell, "CreateShortcut", filename);
            var tempFile = GetValue(shotcutType, srcLnk, "TargetPath");
            var tempArgs = GetValue(shotcutType, srcLnk, "Arguments");

            var desc = GetValue(shotcutType, srcLnk, "Description");

            var description = desc == null || string.IsNullOrEmpty(desc.ToString()) ? name : desc;
            var workDir = GetValue(shotcutType, srcLnk, "WorkingDirectory");

            // 修改路径
            SetValue(shotcutType, srcLnk, "TargetPath", Application.ExecutablePath);
            SetValue(shotcutType, srcLnk, "Arguments", string.Format("-run \"{0} {1}\"", tempFile, tempArgs));

            // 添加描述
            SetValue(shotcutType, srcLnk, "Description", description);
            SetValue(shotcutType, srcLnk, "IconLocation", string.Format("{0}, 0", filename));
            SetValue(shotcutType, srcLnk, "WorkingDirectory", workDir);

            InvokeMethod(shotcutType, srcLnk, "Save");
        }


        /// <summary>
        /// 显示使用说明
        /// </summary>
        private static void Usage()
        {
            MessageBox.Show(Properties.Resources.help, "asadmin 使用说明");
        }

        private static void CheckDependency()
        {
            var file = DEPENDENCY_FILE;
            if (File.Exists(file))
            {
                return;
            }

            File.WriteAllBytes(file, Properties.Resources.Interop_IWshRuntimeLibrary);
        }

        private static void SetValue(Type type, object instance, string fieldName, object value)
        {
            var property = type.GetProperty(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);
            property.SetValue(instance, value, null);
        }

        private static object GetValue(Type type, object instance, string fieldName)
        {
            var property = type.GetProperty(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
            return property.GetValue(instance, null);
        }

        private static object InvokeMethod(Type type, object instance, string methodName, params object[] arguments)
        {
            return type.InvokeMember(methodName,
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
                null,
                instance,
                arguments.Length == 0 ? null : arguments
            );
        }
    }
}
